﻿// -------------------------------------------------------------------------
//    @FileName         :    NFCacheDataSaveTrans.cpp
//    @Author           :    Gao.Yi
//    @Date             :    2025-08-14
//    @Email			:    445267987@qq.com
//    @Module           :    NFCacheDataSaveTrans
//    @Desc             :    缓存数据保存事务基类实现
//                           提供缓存数据保存的统一接口，支持将缓存数据持久化到存储服务器
//                           作为事务框架的基础组件，支持异步保存操作
//
// -------------------------------------------------------------------------

#include "NFCacheDataSaveTrans.h"

#include "NFCacheData.h"
#include "NFCacheDataGetTrans.h"
#include "NFIServerMessageModule.h"
#include "NFComm/NFCore/NFServerTime.h"

/**
 * @brief 构造函数，根据共享内存模式进行初始化
 * 
 * 根据共享内存管理器的创建模式选择初始化方式：
 * - EN_OBJ_MODE_INIT：冷启动模式，调用 CreateInit() 进行完整初始化
 * - 其他模式：热启动模式，调用 ResumeInit() 进行恢复初始化
 */
NFCacheDataSaveTrans::NFCacheDataSaveTrans()
{
    if (EN_OBJ_MODE_INIT == NFShmMgr::Instance()->GetCreateMode())
    {
        CreateInit();
    }
    else
    {
        ResumeInit();
    }
}

/**
 * @brief 析构函数
 * 
 * 清理资源，由于使用共享内存管理，通常无需特殊处理
 */
NFCacheDataSaveTrans::~NFCacheDataSaveTrans()
{
}

/**
 * @brief 冷启动创建初始化
 * 
 * 在共享内存冷启动场景下，对新创建的事务对象进行初始化
 * 当前实现为空，如有需要可在此添加初始化逻辑
 * 
 * @return 0 表示成功
 */
int NFCacheDataSaveTrans::CreateInit()
{
    m_reason = 0; // 查询原因/场景标识
    m_roleId = 0; // 目标对象ID
    m_tableId = 0; // 数据表ID
    m_transId = 0; // 上层批量事务ID
    m_saveObjId = 0;
    return 0;
}

/**
 * @brief 热启动恢复初始化
 *
 * 在共享内存热启动场景下，从持久化数据恢复事务状态时调用
 * 当前实现为空，如有需要可在此添加恢复逻辑
 *
 * @return 0 表示成功
 */
int NFCacheDataSaveTrans::ResumeInit()
{
    return 0;
}

/**
     * @brief 初始化缓存数据保存事务的基本参数
     *
     * 此方法用于设置事务的初始状态，包括服务器类型、角色 ID、数据表 ID、
     * 保存对象 ID 和事务 ID。初始化完成后，事务对象可以用于后续的保存操作。
     *
     * @param serverType 服务器类型
     * @param roleId 目标角色的唯一标识 ID
     * @param tableId 数据表 ID
     * @param saveObjId
     * @param transId 上层批量事务的唯一标识 ID
     * @return 0 表示初始化成功
     */
int NFCacheDataSaveTrans::Init(int serverType, uint64_t roleId, int tableId, int saveObjId, int transId)
{
    m_roleId = roleId;
    m_serverType = serverType;
    m_tableId = tableId;
    m_saveObjId = saveObjId;
    m_transId = transId;
    return 0;
}

/**
 * @brief 处理数据库返回的消息响应
 *
 * 根据数据库返回的消息类型和表 ID，分发到具体的处理逻辑。
 * 如果表 ID 不匹配或命令类型未知，则记录错误日志并返回失败。
 * 在处理失败的情况下，调用 SetFinished 方法结束事务。
 *
 * @param pSsMsgRes 指向数据库返回的消息对象
 * @param cmd 数据库返回的命令类型
 * @param tableId 数据表 ID
 * @param seq 消息序列号
 * @param errCode 错误码，表示操作结果
 * @return 0 表示处理成功，非 0 表示失败
 */
int NFCacheDataSaveTrans::HandleDBMsgRes(const google::protobuf::Message* pSsMsgRes, uint32_t cmd, uint32_t tableId, uint32_t seq, int32_t errCode)
{
    int iRetCode = 0;

    // 验证表 ID 是否匹配当前事务
    if (tableId == (uint32_t)m_tableId)
    {
        // 根据命令类型分发处理
        if (cmd == NFrame::NF_STORESVR_S2C_MODIFYOBJ)
        {
            // 处理批量查询结果
            iRetCode = SaveCacheDataRes(dynamic_cast<const NFrame::storesvr_modobj_res*>(pSsMsgRes), seq, errCode);
        }
        else
        {
            // 未知命令类型，记录错误并返回失败
            LOG_ERR(m_roleId, -1, "Unknown command type:{} for tableId:{}", cmd, tableId);
            iRetCode = -1;
        }
    }
    else
    {
        // 表 ID 不匹配，记录错误
        LOG_ERR(0, -1, "tableId:{} not handle, expected:{}", tableId, m_tableId);
        iRetCode = -1;
    }

    // 如果处理失败，结束事务
    if (iRetCode != 0)
    {
        SetFinished(iRetCode);
    }

    return 0;
}

/**
 * @brief 处理事务完成逻辑
 *
 * 在事务完成时调用此方法，处理事务的最终逻辑。
 * 如果事务运行逻辑返回码非零，尝试获取保存对象并通知保存结果。
 * 同时，如果事务 ID 有效，回调上层批量事务的结果处理方法。
 *
 * @param iRunLogicRetCode 事务运行逻辑的返回码
 * @return 0 表示处理成功
 */
int NFCacheDataSaveTrans::HandleTransFinished(int iRunLogicRetCode)
{
    if (m_transId > 0)
    {
        // 根据事务 ID 获取上层批量事务对象
        NFCacheDataGetTrans* pBatchBase = NFCacheDataGetTrans::GetTrans(m_transId);
        CHECK_NULL_WF(m_roleId, pBatchBase, "GetTrans failed, transId:{}", m_transId);

        // 回调上层事务的结果处理方法
        int iRetCode = pBatchBase->SaveCacheDataRes(m_roleId, m_tableId, m_reason, iRunLogicRetCode);
        CHECK_ERR_AND_FIN_TRANS(iRetCode, pBatchBase, "{}::SaveCacheDataRes Failed, ullId:{}, tableId:{}, reason:{}", pBatchBase->GetClassName(), m_roleId, m_tableId, m_reason);
    }
    return 0;
}

/**
 * @brief 保存缓存数据到数据库
 *
 * 此方法负责将缓存数据保存到数据库中。首先检查保存对象指针是否为空，
 * 然后设置保存时间戳，并通过消息模块发送保存请求。
 * 如果请求失败，记录错误并返回失败码。
 *
 * @param pSaveDb 指向保存数据库接口的指针
 * @param iReason 调用原因/场景标识
 * @param stData Protobuf 消息对象，包含需要保存的数据
 * @return 0 表示保存成功，非 0 表示失败
 */
int NFCacheDataSaveTrans::SaveCacheDataReq(NFISaveDb* pSaveDb, int iReason, const google::protobuf::Message& stData)
{
    CHECK_NULL(m_roleId, pSaveDb);
    m_reason = iReason;
    pSaveDb->SetLastSaveDbTime(NF_ADJUST_TIMENOW());
    pSaveDb->SetCurSavingDbTime(NF_ADJUST_TIMENOW());
    NFLogTrace(NF_LOG_DEFAULT, m_roleId, "Ready Save InTo Db:{}", stData.DebugString());

    int iRetCode = FindModule<NFIServerMessageModule>()->SendModifyObjTrans((NF_SERVER_TYPE)m_serverType, m_roleId, stData, m_tableId, GetGlobalId(), pSaveDb->GetAllSeq());
    CHECK_RET(iRetCode, "SendModifyObjTrans Failed!");

    return 0;
}

/**
 * @brief 保存缓存数据到数据库
 *
 * 此方法负责将缓存数据保存到数据库中。首先设置调用原因并记录日志，
 * 然后通过消息模块发送保存请求。如果请求失败，记录错误并返回失败码。
 *
 * @param iReason 调用原因/场景标识
 * @param stData Protobuf 消息对象，包含需要保存的数据
 * @return 0 表示保存成功，非 0 表示失败
 *
 * @note 此方法不直接操作数据库，而是通过消息模块发送保存请求。
 *       确保 `stData` 包含完整且正确的保存信息。
 * @warning 调用前需确保事务对象已正确初始化，避免因未初始化导致的异常行为。
 */
int NFCacheDataSaveTrans::SaveCacheDataReq(int iReason, const google::protobuf::Message& stData)
{
    m_reason = iReason;
    NFLogTrace(NF_LOG_DEFAULT, m_roleId, "Ready Save InTo Db:{}", stData.DebugString());

    int iRetCode = FindModule<NFIServerMessageModule>()->SendModifyObjTrans((NF_SERVER_TYPE)m_serverType, m_roleId, stData, m_tableId, GetGlobalId(), 0);
    CHECK_RET(iRetCode, "SendModifyObjTrans Failed!");

    return 0;
}

/**
 * @brief 处理缓存数据保存结果
 *
 * 此方法用于处理数据库返回的保存结果。如果保存对象 ID 有效且错误码为零，
 * 则获取保存对象并通知保存成功。否则，直接返回错误码。
 *
 * @param pRes 指向数据库返回的保存结果 Protobuf 消息对象
 * @param seq 消息序列号
 * @param errCode 错误码，表示保存操作的结果
 * @return 错误码，0 表示成功
 */
int NFCacheDataSaveTrans::SaveCacheDataRes(const NFrame::storesvr_modobj_res* pRes, int seq, int errCode)
{
    if (m_saveObjId > 0)
    {
        NFObject* pObject = NFCacheData::GetObjByGlobalId(m_saveObjId, true);
        CHECK_NULL_WF(m_roleId, pObject, "NFCacheData::GetObjByGlobalId({}, true) failed", m_saveObjId);
        NFISaveDb* pSaveObj = dynamic_cast<NFISaveDb*>(pObject);
        CHECK_NULL_WF(m_roleId, pSaveObj, " dynamic_cast<NFISaveDb*>(pObject:{}) failed", m_saveObjId);
        if (errCode == 0)
        {
            pSaveObj->OnSaveDb(true, seq);
        }
        else
        {
            pSaveObj->OnSaveDb(false, seq);
        }
    }

    if (errCode == 0)
    {
        SetFinished(0);
    }
    return errCode;
}
